home *** CD-ROM | disk | FTP | other *** search
/ Sun Solutions 1997 April to September / Sun Solutions CD - APR '97 - SEP '97 (704-3778-12 Rev. H)(Sun Microsystems, Inc.)(1997).iso / products / bin / httpd / src / http_dir.c < prev    next >
C/C++ Source or Header  |  1995-05-18  |  18KB  |  702 lines

  1. /*
  2.  * http_dir.c: Handles the on-the-fly html index generation
  3.  * 
  4.  * All code contained herein is covered by the Copyright as distributed
  5.  * in the README file in the main directory of the distribution of 
  6.  * NCSA HTTPD.
  7.  *
  8.  * 03-23-93 Rob McCool
  9.  *    Wrote base code up to release 1.3
  10.  * 
  11.  * 03-12-95 blong
  12.  *      Added patch by Roy T. Fielding <fielding@avron.ICS.UCI.EDU>
  13.  *      to fix missing trailing slash for parent directory 
  14.  */
  15.  
  16.  
  17. /* httpd.h includes proper directory file */
  18. #include "httpd.h"
  19.  
  20. struct ent {
  21.     char *name;
  22.     char *icon;
  23.     char *alt;
  24.     char *desc;
  25.     size_t size;
  26.     time_t lm;
  27.     struct ent *next;
  28. };
  29.  
  30.  
  31. struct item {
  32.     int type;
  33.     char *apply_to;
  34.     char *apply_path;
  35.     char *data;
  36.     struct item *next;
  37. };
  38.  
  39.  
  40. static struct item *icon_list, *alt_list, *desc_list, *ign_list;
  41. static struct item *hdr_list, *rdme_list, *opts_list;
  42.  
  43. static int dir_opts;
  44.  
  45. void init_indexing() {
  46.     icon_list = NULL;
  47.     alt_list = NULL;
  48.     desc_list = NULL;
  49.     ign_list = NULL;
  50.  
  51.     hdr_list = NULL;
  52.     rdme_list = NULL;
  53.     opts_list = NULL;
  54. }
  55.  
  56. void kill_item_list(struct item *p) {
  57.     struct item *q;
  58.  
  59.     while(p) {
  60.         if(p->apply_to) free(p->apply_to);
  61.         if(p->apply_path) free(p->apply_path);
  62.         if(p->data) free(p->data);
  63.         q = p;
  64.         p = p->next;
  65.         free(q);
  66.     }
  67. }
  68.  
  69. void kill_indexing() {
  70.     kill_item_list(icon_list);
  71.     kill_item_list(alt_list);
  72.     kill_item_list(desc_list);
  73.     kill_item_list(ign_list);
  74.  
  75.     kill_item_list(hdr_list);
  76.     kill_item_list(rdme_list);
  77.     kill_item_list(opts_list);
  78. }
  79.  
  80. struct item *new_item(int type, char *to, char *path, char *data, FILE *out) 
  81. {
  82.     struct item *p;
  83.  
  84.     if(!(p = (struct item *)malloc(sizeof(struct item))))
  85.         die(NO_MEMORY,"new_item",out);
  86.  
  87.     p->type = type;
  88.     if(data) {
  89.         if(!(p->data = strdup(data)))
  90.             die(NO_MEMORY,"new_item",out);
  91.     } else
  92.         p->data = NULL;
  93.  
  94.     if(to) {
  95.         if(!(p->apply_to = (char *)malloc(strlen(to) + 2)))
  96.             die(NO_MEMORY,"new_item",out);
  97.         if((type == BY_PATH) && (!is_matchexp(to))) {
  98.             p->apply_to[0] = '*';
  99.             strcpy(&p->apply_to[1],to);
  100.         } else
  101.             strcpy(p->apply_to,to);
  102.     } else
  103.         p->apply_to = NULL;
  104.  
  105.     if(!(p->apply_path = (char *)malloc(strlen(path) + 2)))
  106.         die(NO_MEMORY,"new_item",out);
  107.     sprintf(p->apply_path,"%s*",path);
  108.  
  109.     return p;
  110. }
  111.  
  112. void add_alt(int type, char *alt, char *to, char *path, FILE *out) {
  113.     struct item *p;
  114.  
  115.     if(type == BY_PATH) {
  116.         if(!strcmp(to,"**DIRECTORY**"))
  117.             strcpy(to,"^^DIRECTORY^^");
  118.     }
  119.     p = new_item(type,to,path,alt,out);
  120.     p->next = alt_list;
  121.     alt_list = p;
  122. }
  123.  
  124. void add_icon(int type, char *icon, char *to, char *path, FILE *out) {
  125.     struct item *p;
  126.     char iconbak[MAX_STRING_LEN];
  127.  
  128.     strcpy(iconbak,icon);
  129.     if(icon[0] == '(') {
  130.         char alt[MAX_STRING_LEN];
  131.         getword(alt,iconbak,',');
  132.         add_alt(type,&alt[1],to,path,out);
  133.         iconbak[strlen(iconbak) - 1] = '\0';
  134.     }
  135.     if(type == BY_PATH) {
  136.         if(!strcmp(to,"**DIRECTORY**"))
  137.             strcpy(to,"^^DIRECTORY^^");
  138.     }
  139.     p = new_item(type,to,path,iconbak,out);
  140.     p->next = icon_list;
  141.     icon_list = p;
  142. }
  143.  
  144. void add_desc(int type, char *desc, char *to, char *path, FILE *out) {
  145.     struct item *p;
  146.  
  147.     p = new_item(type,to,path,desc,out);
  148.     p->next = desc_list;
  149.     desc_list = p;
  150. }
  151.  
  152. void add_ignore(char *ext, char *path, FILE *out) {
  153.     struct item *p;
  154.  
  155.     p = new_item(0,ext,path,NULL,out);
  156.     p->next = ign_list;
  157.     ign_list = p;
  158. }
  159.  
  160. void add_header(char *name, char *path, FILE *out) {
  161.     struct item *p;
  162.  
  163.     p = new_item(0,NULL,path,name,out);
  164.     p->next = hdr_list;
  165.     hdr_list = p;
  166. }
  167.  
  168. void add_readme(char *name, char *path, FILE *out) {
  169.     struct item *p;
  170.  
  171.     p = new_item(0,NULL,path,name,out);
  172.     p->next = rdme_list;
  173.     rdme_list = p;
  174. }
  175.  
  176.  
  177. void add_opts_int(int opts, char *path, FILE *out) {
  178.     struct item *p;
  179.  
  180.     p = new_item(0,NULL,path,NULL,out);
  181.     p->type = opts;
  182.     p->next = opts_list;
  183.     opts_list = p;
  184. }
  185.  
  186. void add_opts(char *optstr, char *path, FILE *out) {
  187.     char w[MAX_STRING_LEN];
  188.     int opts = 0;
  189.  
  190.     while(optstr[0]) {
  191.         cfg_getword(w,optstr);
  192.         if(!strcasecmp(w,"FancyIndexing"))
  193.             opts |= FANCY_INDEXING;
  194.         else if(!strcasecmp(w,"IconsAreLinks"))
  195.             opts |= ICONS_ARE_LINKS;
  196.         else if(!strcasecmp(w,"ScanHTMLTitles"))
  197.             opts |= SCAN_HTML_TITLES;
  198.         else if(!strcasecmp(w,"SuppressLastModified"))
  199.             opts |= SUPPRESS_LAST_MOD;
  200.         else if(!strcasecmp(w,"SuppressSize"))
  201.             opts |= SUPPRESS_SIZE;
  202.         else if(!strcasecmp(w,"SuppressDescription"))
  203.             opts |= SUPPRESS_DESC;
  204.         else if(!strcasecmp(w,"None"))
  205.             opts = 0;
  206.     }
  207.     add_opts_int(opts,path,out);
  208. }
  209.  
  210.  
  211. char *find_item(struct item *list, char *path, int path_only) {
  212.     struct item *p = list;
  213.  
  214.     while(p) {
  215.         /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
  216.         if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) {
  217.             if(!(p->apply_to))
  218.                 return p->data;
  219.             else if(p->type == BY_PATH) {
  220.                 if(!strcmp_match(path,p->apply_to))
  221.                     return p->data;
  222.             } else if(!path_only) {
  223.                 char pathbak[MAX_STRING_LEN];
  224.  
  225.                 strcpy(pathbak,path);
  226.                 content_encoding[0] = '\0';
  227.                 set_content_type(pathbak);
  228.                 if(!content_encoding[0]) {
  229.                     if(p->type == BY_TYPE) {
  230.                         if(!strcmp_match(content_type,p->apply_to))
  231.                             return p->data;
  232.                     }
  233.                 } else {
  234.                     if(p->type == BY_ENCODING) {
  235.                         if(!strcmp_match(content_encoding,p->apply_to))
  236.                             return p->data;
  237.                     }
  238.                 }
  239.             }
  240.         }
  241.         p = p->next;
  242.     }
  243.     return NULL;
  244. }
  245.  
  246. #define find_icon(p,t) find_item(icon_list,p,t)
  247. #define find_alt(p,t) find_item(alt_list,p,t)
  248. #define find_desc(p) find_item(desc_list,p,0)
  249. #define find_header(p) find_item(hdr_list,p,0)
  250. #define find_readme(p) find_item(rdme_list,p,0)
  251.  
  252.  
  253. int ignore_entry(char *path) {
  254.     struct item *p = ign_list;
  255.  
  256.     while(p) {
  257.         if(!strcmp_match(path,p->apply_path))
  258.             if(!strcmp_match(path,p->apply_to))
  259.                 return 1;
  260.         p = p->next;
  261.     }
  262.     return 0;
  263. }
  264.  
  265. int find_opts(char *path) {
  266.     struct item *p = opts_list;
  267.  
  268.     while(p) {
  269.         if(!strcmp_match(path,p->apply_path))
  270.             return p->type;
  271.         p = p->next;
  272.     }
  273.     return 0;
  274. }
  275.  
  276. int insert_readme(char *name, char *readme_fname, int rule, FILE *fd) {
  277.     char fn[MAX_STRING_LEN];
  278.     FILE *r;
  279.     struct stat finfo;
  280.     int plaintext=0;
  281.  
  282.     make_full_path(name,readme_fname,fn);
  283.     strcat(fn,".html");
  284.     if(stat(fn,&finfo) == -1) {
  285.         fn[strlen(fn)-5] = '\0';
  286.         if(stat(fn,&finfo) == -1)
  287.             return 0;
  288.         plaintext=1;
  289.         if(rule) bytes_sent += fprintf(fd,"<HR>%c",LF);
  290.         bytes_sent += fprintf(fd,"<PRE>%c",LF);
  291.     }
  292.     else if(rule) bytes_sent += fprintf(fd,"<HR>%c",LF);
  293.     if(!(r = fopen(fn,"r")))
  294.         return 0;
  295.     send_fd(r,fd,NULL);
  296.     fclose(r);
  297.     if(plaintext)
  298.         bytes_sent += fprintf(fd,"</PRE>%c",LF);
  299.     return 1;
  300. }
  301.  
  302.  
  303. char *find_title(char *filename) {
  304.     char titlebuf[MAX_STRING_LEN], *find = "<TITLE>";
  305.     char filebak[MAX_STRING_LEN];
  306.     FILE *thefile;
  307.     int x,y,n,p;
  308.  
  309.     content_encoding[0] = '\0';
  310.     strcpy(filebak,filename);
  311.     set_content_type(filebak);
  312.     if(((!strcmp(content_type,"text/html")) ||
  313.         (strcmp(content_type,INCLUDES_MAGIC_TYPE)==0))
  314.        && (!content_encoding[0]))
  315.        {
  316.         if(!(thefile = fopen(filename,"r")))
  317.             return NULL;
  318.         n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile);
  319.         titlebuf[n] = '\0';
  320.         for(x=0,p=0;titlebuf[x];x++) {
  321.             if(toupper(titlebuf[x]) == find[p]) {
  322.                 if(!find[++p]) {
  323.                     if((p = ind(&titlebuf[++x],'<')) != -1)
  324.                         titlebuf[x+p] = '\0';
  325.                     /* Scan for line breaks for Tanmoy's secretary */
  326.                     for(y=x;titlebuf[y];y++)
  327.                         if((titlebuf[y] == CR) || (titlebuf[y] == LF))
  328.                             titlebuf[y] = ' ';
  329.             fclose(thefile);
  330.                     return strdup(&titlebuf[x]);
  331.                 }
  332.             } else p=0;
  333.         }
  334.     fclose(thefile);
  335.         return NULL;
  336.     }
  337.     content_encoding[0] = '\0';
  338.     return NULL;
  339. }
  340.  
  341.  
  342. void escape_html(char *fn) {
  343.     register int x,y;
  344.     char copy[MAX_STRING_LEN];
  345.  
  346.     strcpy(copy,fn);
  347.     for(x=0,y=0;copy[y];x++,y++) {
  348.         if(copy[y] == '<') {
  349.             strcpy(&fn[x],"<");
  350.             x+=3;
  351.         }
  352.         else if(copy[y] == '>') {
  353.             strcpy(&fn[x],">");
  354.             x+=3;
  355.         }
  356.         else if(copy[y] == '&') {
  357.             strcpy(&fn[x],"&");
  358.             x+=4;
  359.         }
  360.         else
  361.             fn[x] = copy[y];
  362.     }
  363.     fn[x] = '\0';
  364. }
  365.  
  366. struct ent *make_dir_entry(char *path, char *name, FILE *out) {
  367.     struct ent *p;
  368.     struct stat finfo;
  369.     char t[MAX_STRING_LEN];
  370.     char *tmp;
  371.  
  372.     if((name[0] == '.') && (!name[1]))
  373.         return(NULL);
  374.  
  375.     make_full_path(path,name,t);
  376.  
  377.     if(ignore_entry(t))
  378.         return(NULL);
  379.  
  380.     if(!(p=(struct ent *)malloc(sizeof(struct ent))))
  381.         die(NO_MEMORY,"make_dir_entry",out);
  382.     if(!(p->name=(char *)malloc(strlen(name) + 2)))
  383.         die(NO_MEMORY,"make_dir_entry",out);
  384.  
  385.     if(dir_opts & FANCY_INDEXING) {
  386.         if((!(dir_opts & FANCY_INDEXING)) || stat(t,&finfo) == -1) {
  387.             strcpy(p->name,name);
  388.             p->size = -1;
  389.             p->icon = NULL;
  390.             p->alt = NULL;
  391.             p->desc = NULL;
  392.             p->lm = -1;
  393.         }
  394.         else {
  395.             p->lm = finfo.st_mtime;
  396.         p->size = -1;
  397.         p->icon = NULL;
  398.         p->alt = NULL;
  399.         p->desc = NULL;
  400.             if(S_ISDIR(finfo.st_mode)) {
  401.                 if(!(p->icon = find_icon(t,1)))
  402.             if (p->icon != NULL) free(p->icon);
  403.                     p->icon = find_icon("^^DIRECTORY^^",1);
  404.                 if(!(tmp = find_alt(t,1))){
  405.                     p->alt = (char *) malloc(sizeof(char)*4);
  406.                     strcpy(p->alt,"DIR");
  407.         } else {
  408.           p->alt = strdup(tmp);
  409.                 }
  410.                 p->size = -1;
  411.                 strncpy_dir(p->name,name,MAX_STRING_LEN);
  412.             }
  413.             else {
  414.                 p->icon = find_icon(t,0);
  415.                 tmp = find_alt(t,0);
  416.         if (tmp != NULL) p->alt = strdup(tmp);
  417.                 p->size = finfo.st_size;
  418.                 strcpy(p->name,name);
  419.             }
  420.         }
  421.         if(p->desc = find_desc(t))
  422.             p->desc = strdup(p->desc);
  423.         if((!p->desc) && (dir_opts & SCAN_HTML_TITLES))
  424.             p->desc = find_title(t);
  425.     }
  426.     else {
  427.         p->icon = NULL;
  428.         p->alt = NULL;
  429.         p->desc = NULL;
  430.         p->size = -1;
  431.         p->lm = -1;
  432.         strcpy(p->name,name);
  433.     }
  434.     return(p);
  435. }
  436.  
  437.  
  438. void send_size(size_t size, FILE *fd) {
  439.  
  440.     if(size == -1) {
  441.         fputs("    -",fd);
  442.         bytes_sent += 5;
  443.     }
  444.     else {
  445.         if(!size) {
  446.             fputs("   0K",fd);
  447.             bytes_sent += 5;
  448.         }
  449.         else if(size < 1024) {
  450.             fputs("   1K",fd);
  451.             bytes_sent += 5;
  452.         }
  453.         else if(size < 1048576)
  454.             bytes_sent += fprintf(fd,"%4dK",size / 1024);
  455.         else
  456.             bytes_sent += fprintf(fd,"%4dM",size / 1048576);
  457.     }
  458. }
  459.  
  460. void terminate_description(char *desc) {
  461.     int maxsize = 23;
  462.     register int x;
  463.     
  464.     if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17;
  465.     if(dir_opts & SUPPRESS_SIZE) maxsize += 7;
  466.  
  467.     for(x=0;desc[x] && maxsize;x++) {
  468.         if(desc[x] == '<') {
  469.             while(desc[x] != '>') {
  470.                 if(!desc[x]) {
  471.                     maxsize = 0;
  472.                     break;
  473.                 }
  474.                 ++x;
  475.             }
  476.         }
  477.         else --maxsize;
  478.     }
  479.     if(!maxsize) {
  480.         desc[x] = '>';
  481.         desc[x+1] = '\0';
  482.     }
  483.  
  484. }
  485.  
  486. void output_directories(struct ent **ar,int n,char *name,FILE *fd)
  487. {
  488.     int x;
  489.     char anchor[2 * MAX_STRING_LEN + 64],t[MAX_STRING_LEN],t2[MAX_STRING_LEN];
  490.     char *tp;
  491.  
  492.     if(name[0] == '\0') { 
  493.         name[0] = '/'; name[1] = '\0'; 
  494.     }
  495.     /* aaaaargh Solaris sucks. */
  496.     fflush(fd);
  497.  
  498.     if(dir_opts & FANCY_INDEXING) {
  499.         fputs("<PRE>",fd);
  500.         bytes_sent += 5;
  501.         if(tp = find_icon("^^BLANKICON^^",1))
  502.             bytes_sent += (fprintf(fd,"<IMG SRC=\"%s\" ALT=\"     \"> ",tp));
  503.         bytes_sent += fprintf(fd,"Name                   ");
  504.         if(!(dir_opts & SUPPRESS_LAST_MOD))
  505.             bytes_sent += fprintf(fd,"Last modified     ");
  506.         if(!(dir_opts & SUPPRESS_SIZE))
  507.             bytes_sent += fprintf(fd,"Size  ");
  508.         if(!(dir_opts & SUPPRESS_DESC))
  509.             bytes_sent += fprintf(fd,"Description");
  510.         bytes_sent += fprintf(fd,"%c<HR>%c",LF,LF);
  511.     }
  512.     else {
  513.         fputs("<UL>",fd);
  514.         bytes_sent += 4;
  515.     }
  516.         
  517.     for(x=0;x<n;x++) {
  518.         if((!strcmp(ar[x]->name,"../")) || (!strcmp(ar[x]->name,".."))) {
  519.  
  520. /* Fixes trailing slash for fancy indexing.  Thanks Roy ? */
  521.             make_full_path(name,"../",t);
  522.             getparents(t);
  523.             if(t[0] == '\0') {
  524.                 t[0] = '/'; t[1] = '\0';
  525.             }
  526.             escape_uri(t);
  527.             sprintf(anchor,"<A HREF=\"%s\">",t);
  528.             strcpy(t2,"Parent Directory</A>");
  529.         }
  530.         else {
  531.             lim_strcpy(t,ar[x]->name, MAX_STRING_LEN);
  532.             strcpy(t2,t);
  533.             if(strlen(t2) > 21) {
  534.                 t2[21] = '>';
  535.                 t2[22] = '\0';
  536.             }
  537.             /* screws up formatting, but some need it. should fix. */
  538. /*            escape_html(t2); */
  539.             strcat(t2,"</A>");
  540.             escape_uri(t);
  541.             sprintf(anchor,"<A NAME=\"%s\" HREF=\"%s\">",t,t);
  542.         }
  543.         escape_url(t);
  544.  
  545.         if(dir_opts & FANCY_INDEXING) {
  546.             if(dir_opts & ICONS_ARE_LINKS)
  547.                 bytes_sent += fprintf(fd,"%s",anchor);
  548.             if((ar[x]->icon) || default_icon[0] || local_default_icon[0]) {
  549.                 bytes_sent += fprintf(fd,"<IMG SRC=\"%s\" ALT=\"[%s]\">",
  550.                                       ar[x]->icon ? ar[x]->icon : 
  551.                        local_default_icon[0] ? 
  552.                     local_default_icon : default_icon,
  553.                                       ar[x]->alt ? ar[x]->alt : "   ");
  554.             }
  555.             if(dir_opts & ICONS_ARE_LINKS) {
  556.                 fputs("</A>",fd);
  557.                 bytes_sent += 4;
  558.             }
  559.             bytes_sent += fprintf(fd," %s",anchor);
  560.             bytes_sent += fprintf(fd,"%-27.27s",t2);
  561.             if(!(dir_opts & SUPPRESS_LAST_MOD)) {
  562.                 if(ar[x]->lm != -1) {
  563.                     struct tm *ts = localtime(&ar[x]->lm);
  564.                     strftime(t,MAX_STRING_LEN,"%d-%b-%y %H:%M  ",ts);
  565.                     fputs(t,fd);
  566.                     bytes_sent += strlen(t);
  567.                 }
  568.                 else {
  569.                     fputs("                 ",fd);
  570.                     bytes_sent += 17;
  571.                 }
  572.             }
  573.             if(!(dir_opts & SUPPRESS_SIZE)) {
  574.                 send_size(ar[x]->size,fd);
  575.                 fputs("  ",fd);
  576.                 bytes_sent += 2;
  577.             }
  578.             if(!(dir_opts & SUPPRESS_DESC)) {
  579.                 if(ar[x]->desc) {
  580.                     terminate_description(ar[x]->desc);
  581.                     bytes_sent += fprintf(fd,"%s",ar[x]->desc);
  582.                 }
  583.             }
  584.         }
  585.         else
  586.             bytes_sent += fprintf(fd,"<LI> %s %s",anchor,t2);
  587.         fputc(LF,fd);
  588.         ++bytes_sent;
  589.     }
  590.     if(dir_opts & FANCY_INDEXING) {
  591.         fputs("</PRE>",fd);
  592.         bytes_sent += 6;
  593.     }
  594.     else {
  595.         fputs("</UL>",fd);
  596.         bytes_sent += 5;
  597.     }
  598. }
  599.  
  600.  
  601. int dsortf(struct ent **s1,struct ent **s2)
  602. {
  603.     return(strcmp((*s1)->name,(*s2)->name));
  604. }
  605.  
  606.     
  607. void index_directory(char *name, FILE *fd)
  608. {
  609.     DIR *d;
  610.     struct DIR_TYPE *dstruct;
  611.     int num_ent=0,x;
  612.     struct ent *head,*p,*q;
  613.     struct ent **ar;
  614.     char unmunged_name[HUGE_STRING_LEN];
  615.     char *tmp;
  616.  
  617.     strncpy_dir(unmunged_name,name,HUGE_STRING_LEN);
  618.     unmunge_name(unmunged_name);
  619.  
  620.     if(!(d=opendir(name)))
  621.         die(FORBIDDEN,unmunged_name,fd);
  622.  
  623.     strcpy(content_type,"text/html");
  624.     if(!assbackwards) 
  625.         send_http_header(fd);
  626.  
  627.     if(header_only) {
  628.       closedir(d);
  629.       return;
  630.     }
  631.  
  632.     bytes_sent = 0;
  633.  
  634.     dir_opts = find_opts(name);
  635.  
  636. /* Spew HTML preamble */
  637.     bytes_sent += fprintf(fd,"<HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>",
  638.                           unmunged_name);
  639.     fputc(LF,fd);
  640.     ++bytes_sent;
  641.  
  642.     if((!(tmp = find_header(name))) || (!(insert_readme(name,tmp,0,fd))))
  643.         bytes_sent += fprintf(fd,"<H1>Index of %s</H1>%c",unmunged_name,LF);
  644.  
  645. /* 
  646.  * Since we don't know how many dir. entries there are, put them into a 
  647.  * linked list and then arrayificate them so qsort can use them. 
  648.  */
  649.     head=NULL;
  650.     while(dstruct=readdir(d)) {
  651.         if(p = make_dir_entry(name,dstruct->d_name,fd)) {
  652.             p->next = head;
  653.             head = p;
  654.             num_ent++;
  655.         }
  656.     }
  657.     if(!(ar=(struct ent **) malloc(num_ent*sizeof(struct ent *)))) {
  658.     closedir(d);
  659.         die(NO_MEMORY,"index_directory",fd);
  660.     }
  661.     p=head;
  662.     x=0;
  663.     while(p) {
  664.         ar[x++]=p;
  665.         p = p->next;
  666.     }
  667.     
  668.     qsort((void *)ar,num_ent,sizeof(struct ent *),
  669. #ifdef ULTRIX_BRAIN_DEATH
  670.           (int (*))dsortf);
  671. #else
  672.           (int (*)(const void *,const void *))dsortf);
  673. #endif
  674.      output_directories(ar,num_ent,unmunged_name,fd);
  675.      free(ar);
  676.      q = head;
  677.      while(q) {
  678.          p=q->next;
  679.          free(q->name);
  680.          if(q->desc)
  681.              free(q->desc);
  682.      if(q->alt)
  683.          free(q->alt);
  684.          free(q);
  685.          q=p;
  686.      }
  687.      closedir(d);
  688.      if(dir_opts & FANCY_INDEXING)
  689.          if(tmp = find_readme(name))
  690.              insert_readme(name,tmp,1,fd);
  691.      else {
  692.          fputs("</UL>",fd);
  693.          bytes_sent += 5;
  694.      }
  695.  
  696.      fputs("</BODY>",fd);
  697.      fflush(fd);
  698.      bytes_sent += 7;
  699.      fflush(fd);
  700.      log_transaction();
  701. }
  702.